package com.liuyjy.base.common.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

/**
 * java 8 LocalDate /LocalDateTime
 * Instant         时间戳
 * Duration        持续时间、时间差
 * LocalDate       只包含日期，比如：2018-09-24
 * LocalTime       只包含时间，比如：10:32:10
 * LocalDateTime   包含日期和时间，比如：2018-09-24 10:32:10
 * Peroid          时间段
 * ZoneOffset      时区偏移量，比如：+8:00
 * ZonedDateTime   带时区的日期时间
 * Clock           时钟，可用于获取当前时间戳
 * java.time.format.DateTimeFormatter      时间格式化类
 *
 * @Author liuyjy
 * @Date 2019/8/15
 * @Description: 日期处理
 **/
@Slf4j
public class DateUtil {

    public static final String DATETIME = "yyyy-MM-dd HH:mm:ss";
    public static final String YYYYMMDD = "yyyyMMdd";
    public static final String DATE = "yyyy-MM-dd";
    public static final String TIME = "HH:mm:ss";

    /**
     * 获得当前时间，yyyy-MM-dd HH:mm:ss
     *
     * @return String
     */
    public static String dateFormat() {
        return dateFormat(LocalDateTime.now(), DATETIME);
    }

    /**
     * 获得当前时间，格式自定义
     *
     * @param format 格式
     * @return String
     */
    public static String dateFormat(String format) {
        return dateFormat(LocalDateTime.now(), format);
    }

    /**
     * 日期格式化  yyyy-MM-dd HH:mm:ss
     *
     * @param localDateTime 日期
     * @return String
     */
    public static String dateFormat(LocalDateTime localDateTime) {
        // 格式化日期
        return dateFormat(localDateTime, DATETIME);
    }

    /**
     * 日期格式化  yyyy-MM-dd
     *
     * @param localDate 日期
     * @return String
     */
    public static String dateFormat(LocalDate localDate) {
        // 格式化日期
        return dateFormat(localDate, DATE);
    }

    /**
     * 时间格式化 HH:mm:ss
     *
     * @param localTime 日期
     * @return String
     */
    public static String dateFormat(LocalTime localTime) {
        // 格式化日期
        return dateFormat(localTime, TIME);
    }

    /**
     * 日期格式化
     *
     * @param date   日期
     * @param format 格式
     * @return String
     */
    public static String dateFormat(LocalDateTime date, String format) {
        if (null == date || StringUtils.isEmpty(format)) {
            return null;
        }
        // 格式化日期
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return date.format(formatter);
    }

    /**
     * 日期格式化
     *
     * @param date   日期
     * @param format 格式
     * @return String
     */
    public static String dateFormat(LocalDate date, String format) {
        if (null == date || StringUtils.isEmpty(format)) {
            return null;
        }
        // 格式化日期
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return date.format(formatter);
    }


    /**
     * 日期格式化
     *
     * @param date   日期
     * @param format 格式
     * @return String
     */
    public static String dateFormat(LocalTime date, String format) {
        if (null == date || StringUtils.isEmpty(format)) {
            return null;
        }
        // 格式化日期
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return date.format(formatter);
    }

    /**
     * 日期格式化
     *
     * @param date   日期
     * @param format 格式
     * @return String
     */
    public static String dateFormat(Date date, String format) {
        if (null == date || StringUtils.isEmpty(format)) {
            return null;
        }
        LocalDateTime localDateTime = dateToLocalDateTime(date);
        return dateFormat(localDateTime, format);
    }

    /**
     * 日期格式化
     *
     * @param timestamp 日期
     * @param format    格式
     * @return String
     */
    public static String dateFormat(Long timestamp, String format) {
        if (null == timestamp || StringUtils.isEmpty(format)) {
            return null;
        }
        Instant instant = Instant.ofEpochMilli(timestamp);
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return dateFormat(localDateTime, format);
    }

    /**
     * date 转 LocalDateTime
     *
     * @param date date
     * @return LocalDateTime
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        if (null == date) {
            return null;
        }
        //An instantaneous point on the time-line.(时间线上的一个瞬时点。)
        Instant instant = date.toInstant();
        //A time-zone ID, such as {@code Europe/Paris}.(时区)
        ZoneId zoneId = ZoneId.systemDefault();
        return instant.atZone(zoneId).toLocalDateTime();
    }

    /**
     * date 转 LocalDate
     *
     * @param date date
     * @return LocalDate
     */
    public static LocalDate dateToLocalDate(Date date) {
        if (null == date) {
            return null;
        }
        //An instantaneous point on the time-line.(时间线上的一个瞬时点。)
        Instant instant = date.toInstant();
        //A time-zone ID, such as {@code Europe/Paris}.(时区)
        ZoneId zoneId = ZoneId.systemDefault();
        return instant.atZone(zoneId).toLocalDate();
    }

    /**
     * date 转 LocalDate
     *
     * @param date date
     * @return LocalDate
     */
    public static LocalTime dateToLocalTime(Date date) {
        if (null == date) {
            return null;
        }
        //An instantaneous point on the time-line.(时间线上的一个瞬时点。)
        Instant instant = date.toInstant();
        //A time-zone ID, such as {@code Europe/Paris}.(时区)
        ZoneId zoneId = ZoneId.systemDefault();
        return instant.atZone(zoneId).toLocalTime();
    }


    /**
     * LocalDateTime转换为Date
     *
     * @param localDateTime 日期
     * @return Date
     */
    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        if (null == localDateTime) {
            return null;
        }
        ZoneId zoneId = ZoneId.systemDefault();
        //Combines this date-time with a time-zone to create a  ZonedDateTime.
        ZonedDateTime zdt = localDateTime.atZone(zoneId);
        return Date.from(zdt.toInstant());
    }

    /**
     * LocalDate转换为Date
     *
     * @param localDate 日期
     * @return Date
     */
    public static Date localDateToDate(LocalDate localDate) {
        if (null == localDate) {
            return null;
        }
        ZoneId zoneId = ZoneId.systemDefault();
        //Combines this date-time with a time-zone to create a  ZonedDateTime.
        ZonedDateTime zdt = localDate.atStartOfDay().atZone(zoneId);
        return Date.from(zdt.toInstant());
    }

    /**
     * String转时间 yyyy-MM-dd HH:mm:ss
     *
     * @param time 日期字符
     * @return LocalDateTime
     */
    public static LocalDateTime parseStrToDateTime(String time) {
        return parseStrToDateTime(time, DATETIME);
    }

    /**
     * String转时间 yyyy-MM-dd
     *
     * @param time 日期字符
     * @return LocalDate
     */
    public static LocalDate parseStrToDate(String time) {
        return parseStrToDate(time, DATE);
    }

    /**
     * String转时间 HH:mm:ss
     *
     * @param time 日期字符
     * @return LocalTime
     */
    public static LocalTime parseStrToTime(String time) {
        return parseStringToTime(time, TIME);
    }

    /**
     * String转时间 yyyy-MM-dd HH:mm:ss
     *
     * @param time 日期字符
     * @return LocalDateTime
     */
    public static Date strToDate(String time) {
        return strToDate(time, DATETIME);
    }

    /**
     * String转时间
     *
     * @param time   日期字符
     * @param format 格式
     * @return LocalDateTime
     */
    public static Date strToDate(String time, String format) {
        if (StringUtils.isEmpty(time) || StringUtils.isEmpty(format)) {
            return null;
        }
        SimpleDateFormat formatter = new SimpleDateFormat(format);
        ParsePosition pos = new ParsePosition(0);
        return formatter.parse(time, pos);
    }

    /**
     * String转时间
     *
     * @param time   日期字符
     * @param format 格式
     * @return LocalDateTime
     */
    public static LocalDateTime parseStrToDateTime(String time, String format) {
        if (StringUtils.isEmpty(time) || StringUtils.isEmpty(format)) {
            return null;
        }
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format);
        return LocalDateTime.parse(time, df);
    }

    /**
     * String转时间
     *
     * @param time   日期字符
     * @param format 格式
     * @return LocalDate
     */
    public static LocalDate parseStrToDate(String time, String format) {
        if (StringUtils.isEmpty(time) || StringUtils.isEmpty(format)) {
            return null;
        }
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format);
        return LocalDate.parse(time, df);
    }

    /**
     * String转时间
     *
     * @param time   日期字符
     * @param format 格式
     * @return LocalTime
     */
    public static LocalTime parseStringToTime(String time, String format) {
        if (StringUtils.isEmpty(time) || StringUtils.isEmpty(format)) {
            return null;
        }
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format);
        return LocalTime.parse(time, df);
    }

    /**
     * 时间转时间戳
     *
     * @return long
     */
    public static Long getTimestamp() {
        return getTimestamp(LocalDateTime.now());
    }

    /**
     * 时间转时间戳
     *
     * @param localDateTime 日期
     * @return long
     */
    public static Long getTimestamp(LocalDateTime localDateTime) {
        if (null == localDateTime) {
            return null;
        }
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return instant.toEpochMilli();
    }

    /**
     * 计算两个日期之间相差的天数
     *
     * @param from 开始日期
     * @param to   结束日期
     * @return int
     */
    public static int daysBetweenInt(Date from, Date to) {
        return Math.toIntExact(daysBetweenLong(from, to));
    }

    /**
     * 计算两个日期之间相差的天数
     *
     * @param from 开始日期
     * @param to   结束日期
     * @return int
     */
    public static long daysBetweenLong(Date from, Date to) {
        if (null == from || null == to) {
            return 0;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(from);
        long time1 = cal.getTimeInMillis();
        cal.setTime(to);
        long time2 = cal.getTimeInMillis();
        return (time2 - time1) / (1000 * 3600 * 24);
    }


    /**
     * 日期增加天数
     *
     * @param date 日期
     * @param days 天数
     * @return Date
     */
    public static Date addXDays(Date date, int days) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(5, days);
        return cal.getTime();
    }

    /**
     * 日期相差几天
     *
     * @param fromDate 开始日期
     * @param toDate
     * @return
     */
    public static long calTowDateOfDay(Date fromDate, Date toDate) {
        long nd = 86400000L;
        long diff = toDate.getTime() - fromDate.getTime();
        long day = diff / nd;
        return day;
    }

    public static final java.sql.Date string2Date(String dateString) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
        dateFormat.setLenient(false);
        Date date = null;

        try {
            date = dateFormat.parse(dateString);
        } catch (ParseException var4) {
        }

        return new java.sql.Date(date.getTime());
    }

    public static final Date string2Date(String dateString, String df) {
        DateFormat dateFormat = new SimpleDateFormat(df, Locale.SIMPLIFIED_CHINESE);
        dateFormat.setLenient(false);
        Date date = null;

        try {
            date = dateFormat.parse(dateString);
        } catch (ParseException var5) {
        }

        return date;
    }

    /**
     * 获取当前月份最后一天
     *
     * @return
     * @throws ParseException
     */
    public static String getMaxMonthDate() {
        SimpleDateFormat dft = new SimpleDateFormat("yyyyMMdd");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        // calendar.add(Calendar.MONTH, -1);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        return dft.format(calendar.getTime());
    }

    /**
     * 描述:获取下一个月的第一天.
     *
     * @return
     */
    public static String getPerFirstDayOfMonth() {
        SimpleDateFormat dft = new SimpleDateFormat("yyyyMMdd");
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, 1);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
        return dft.format(calendar.getTime());
    }

    /**
     * 获取任意时间的月的最后一天
     * 描述:<描述函数实现的功能>.
     *
     * @param repeatDate
     * @return
     */
    private static String getMaxMonthDate(String repeatDate) {
        SimpleDateFormat dft = new SimpleDateFormat("yyyyMMdd");
        Calendar calendar = Calendar.getInstance();
        try {
            if (StringUtils.isNotBlank(repeatDate) && !"null".equals(repeatDate)) {
                calendar.setTime(dft.parse(repeatDate));
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        // calendar.add(Calendar.MONTH, -1);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        return dft.format(calendar.getTime());
    }

    /**
     * 获取任意时间的月第一天
     * 描述:<描述函数实现的功能>.
     *
     * @param repeatDate
     * @return
     */
    private static String getMinMonthDate(String repeatDate) {
        SimpleDateFormat dft = new SimpleDateFormat("yyyyMMdd");
        Calendar calendar = Calendar.getInstance();
        try {
            if (StringUtils.isNotBlank(repeatDate) && !"null".equals(repeatDate)) {
                calendar.setTime(dft.parse(repeatDate));
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        // calendar.add(Calendar.MONTH, -1);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
        return dft.format(calendar.getTime());
    }

    /**
     * 获取任意时间的下一个月
     * 描述:<描述函数实现的功能>.
     *
     * @param repeatDate
     * @return
     */
    public static String getPreMonth(String repeatDate) {
        String lastMonth = "";
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat dft = new SimpleDateFormat("yyyy-MM");
        int year = Integer.parseInt(repeatDate.substring(0, 4));
        String monthsString = repeatDate.substring(5, 7);
        int month;
        if ("0".equals(monthsString.substring(0, 1))) {
            month = Integer.parseInt(monthsString.substring(1, 2));
        } else {
            month = Integer.parseInt(monthsString.substring(0, 2));
        }
        cal.set(year, month, Calendar.DATE);
        lastMonth = dft.format(cal.getTime());
        return lastMonth;
    }

    /**
     * 计算时间
     *
     * @param dateTime       处理时间
     * @param onlineTime     追加时间
     * @param onlineTimeType 有效期单位：1、年 2、月 3、日 4、时
     * @return
     */
    public static LocalDateTime assemblyDate(LocalDateTime dateTime, Integer onlineTime, Integer onlineTimeType) {
        if (null == onlineTime || null == onlineTimeType) {
            return null;
        }
        switch (onlineTimeType) {
            case 1:
                //年
                dateTime = dateTime.plusYears(onlineTime);
                break;
            case 2:
                //月
                dateTime = dateTime.plusMonths(onlineTime);
                break;
            case 3:
                //天
                dateTime = dateTime.plusDays(onlineTime);
                break;
            case 4:
                //月
                dateTime = dateTime.plusHours(onlineTime);
                break;
            default:
                log.error("时间单位不明确:{}", onlineTimeType);
                return dateTime;
            //throw new BusinessException("6000", "时间单位不明确!");
        }
        return dateTime;
    }


    /**
     * 日期 转 时间戳（秒）
     *
     * @param time 日期
     * @return 时间戳
     * @author yinchao
     */
    public static String dateTotimeStampSecond(LocalDateTime time) {
        return String.valueOf(time.toEpochSecond(ZoneOffset.ofHours(+8)));
    }


    /**
     * 时间转换成字符串
     *
     * @param date
     * @return
     */
    public static String getString(Date date) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日");
        String format = dateFormat.format(date);
        return format;
    }

    /**
     * 获取当前日期是星期几<br>
     *
     * @param dt
     * @return 当前日期是星期几
     */
    public static String getWeekOfDate(Date dt) {
        String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(dt);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0)
            w = 0;
        return weekDays[w];
    }

    public static void main(String[] args) {
        System.out.println(strToDate("2023-11-16 18:33:00", DATETIME));
        System.out.println(strToDate("2023-11-16"));
        System.out.println(string2Date("2023-11-16 18:33:00"));
        System.out.println("liuyjy" + dateFormat(LocalDateTime.now()));
        System.out.println(dateFormat(LocalDateTime.now(), TIME));
        System.out.println(dateFormat(LocalDate.now(), "MM-dd"));
        System.out.println(dateFormat(LocalTime.now(), "HH:mm"));
        System.out.println(dateFormat(new Date(), TIME));

        System.out.println(dateToLocalDateTime(new Date()));
        System.out.println(dateToLocalDate(new Date()));
        System.out.println(dateToLocalTime(new Date()));

        System.out.println(localDateTimeToDate(LocalDateTime.now()));
        System.out.println(localDateToDate(LocalDate.now()));

        System.out.println(parseStrToDateTime("2023-11-16 18:33:00", DATETIME));
        System.out.println(parseStrToDate("2023-11-16 18:33:00", DATETIME));
        System.out.println(parseStringToTime("2023-11-16 18:33:00", DATETIME));

        System.out.println(getTimestamp(LocalDateTime.now()));

        System.out.println(dateFormat(1700131863833L, TIME));

        System.out.println(DateUtil.dateFormat(LocalDate.now(), "yyyy年MM月"));
    }
}
